/**
 * 
 */
package neptune.manager.completer;

import neptune.manager.NeptuneManager;
import neptune.manager.builder.BuilderException;
import neptune.manager.builder.IPAddressBuilder;
import neptune.manager.model.experiment.Experiment;
import neptune.manager.model.topology.IPAddress;
import neptune.manager.model.topology.Link;
import neptune.manager.model.topology.NetworkInterface;
import neptune.manager.model.topology.Node;
import neptune.manager.model.topology.Topology;
import neptune.manager.utils.DebugLog;

/**
 * @author Roberto Bifulco [info@RobertoBifulco.it] [RobertoBifulco.it]
 * 
 */
public class OLPILinkCompleter extends LinkCompleter {

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * neptune.manager.completer.LinkCompleter#completeLink(neptune.manager.
	 * model.topology.Link)
	 */
	@Override
	public void completeLink(Link link, Topology actual)
			throws CompleterException {

		Node source = actual.searchNodeIntoTopology(link.getSource()
				.getNodeId());
		Node dest = actual.searchNodeIntoTopology(link.getDestination()
				.getNodeId());

		if (source == null || dest == null)
			throw new CompleterException("Unable to complete link '"
					+ link.getId()
					+ "': unable to retrieve source or destination node");

		NetworkInterface sourceIf = source.getInterfaceByName(link.getSource()
				.getInterfaceId());
		NetworkInterface destIf = dest.getInterfaceByName(link.getDestination()
				.getInterfaceId());

		if (sourceIf == null || destIf == null)
			throw new CompleterException("Unable to complete link '"
					+ link.getId() + "': unable to retrieve source interface"
					+ " or destination interface");

		if (sourceIf.getIpAddresses().length > 1
				|| destIf.getIpAddresses().length > 1)
			throw new CompleterException(
					"Only OLPI link multiplexing tech is supported");

		try {
			Experiment exp = NeptuneManager.instance().getExperiment(
					actual.getName());

			String linksSubnet = NeptuneManager.instance().getLinksSubnetMask();

			ipAddresssGeneration(sourceIf, destIf, exp.getAssignedSubnet(),
					linksSubnet, actual);
		} catch (Exception e) {
			throw new CompleterException("Error in link '" + link.getId()
					+ "':" + e.getMessage());
		}

	}

	private void ipAddresssGeneration(NetworkInterface sourceIf,
			NetworkInterface destIf, IPAddress expSubnet, String linkSubnet,
			Topology actualTopology) throws CompleterException {
		IPAddress sourceIP = null;
		IPAddress destIP = null;

		if (sourceIf.getIpAddresses().length == 1)
			sourceIP = sourceIf.getIpAddresses()[0];
		if (destIf.getIpAddresses().length == 1)
			destIP = destIf.getIpAddresses()[0];

		if (sourceIP != null && destIP != null) {
			IPAddress sourceSub = sourceIP.getSubnetAddress();
			IPAddress destSub = sourceIP.getSubnetAddress();
			if (!sourceSub.equals(destSub)) {
				throw new CompleterException(
						"EndPoints ip addresses are not on the same subnet: "
								+ sourceIP + ", " + destIP);
			}
			return;
		}

		try {
			if (sourceIP != null) {
				destIP = IPAddressBuilder.buildUniqueIP(sourceIP
						.getSubnetAddress());
				destIf.addIpAddress(destIP);
				return;
			}

			if (destIP != null) {
				sourceIP = IPAddressBuilder.buildUniqueIP(destIP
						.getSubnetAddress());
				sourceIf.addIpAddress(sourceIP);
				return;
			}
		} catch (BuilderException e1) {
			throw new CompleterException(e1);
		}

		try {
			IPAddress linkSubnetAddr = IPAddressBuilder
					.buildUniqueSubnetAddress(expSubnet, linkSubnet,
							actualTopology);

			if (linkSubnetAddr == null)
				throw new CompleterException("Unable to create link subnet");

			sourceIP = IPAddressBuilder.buildUniqueIP(linkSubnetAddr);
			sourceIf.addIpAddress(sourceIP);

			destIP = IPAddressBuilder.buildUniqueIP(linkSubnetAddr,
					new IPAddress[] { sourceIP });
			destIf.addIpAddress(destIP);
		} catch (Exception e) {
			DebugLog.printDetailedLog(e.getMessage(), OLPILinkCompleter.class
					.getSimpleName());
			throw new CompleterException(e);
		}

	}
}
